﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;

/// <summary>
/// AssetsManager
/// 资源管理器，资源更新、加载、卸载
/// </summary>
public class AssetsManager : MonoBehaviour
{
    /// <summary>
    /// 实际Asset的Cache 
    /// </summary>
    //Dictionary<string, Asset> assetCache = new Dictionary<string, Asset>();
    /// <summary>
    /// 本地AssetCache
    /// </summary>
    Dictionary<string, Asset> localAssetCache = new Dictionary<string, Asset>();
    /// <summary>
    /// 在线AssetCache
    /// </summary>
    Dictionary<string, Asset> remoteAssetCache = new Dictionary<string, Asset>();

    public const string defaultAssetPath = "";
    public const string assetPath = "";

    public const string assetXmlName = "config.xml";
    public const string remoteAssetXmlName = "remote_config.xml";

    #region Setup
    /// <summary>
    /// 是否第一次安装运行
    /// </summary>
    bool isFirstSetup = false;
    public const string firstSetupStr = "firstSetup";

    //int Setup_MaxAssetCount = 0;
    int Setup_FinishAssetCount = 0;
    int Setup_ErrorAssetCount = 0;
    int Setup_DoneAssetCount { get { return Setup_FinishAssetCount + Setup_ErrorAssetCount; } }
#if !UNITY_WEBPLAYER

    void Setup() {
        isFirstSetup = PlayerPrefs.GetInt(firstSetupStr, 0) == 0 ? true : false;
        if (isFirstSetup) {
            // Init LocalAssetCache
            // TODO
            StartCoroutine(CopyDefaultAsestToDynamicAsset());
        }
    }

    IEnumerator CopyDefaultAsestToDynamicAsset() {
        // 若AssetPath中有文件，直接清空
        foreach (var content in Directory.GetFileSystemEntries(assetPath)) {
            if (Directory.Exists(content))
                Directory.Delete(content, true);
            else if (File.Exists(content))
                File.Delete(content);
        }
        //Setup_MaxAssetCount = localAssetCache.Count;
        Setup_FinishAssetCount = 0;
        Setup_ErrorAssetCount = 0;
        foreach (var asset in localAssetCache) {
            string sPath = defaultAssetPath + asset.Key;
            string tPath = assetPath + asset.Key;
#if UNITY_EDITOR_WIN || UNITY_STANDALONE_WIN
            WWW www = new WWW("file:///" + sPath);
#else
            WWW www = new WWW("file://" + sPath);
#endif
            yield return www;
            if (www.error != null) {
                Setup_ErrorAssetCount++;
                Debug.LogError(www.error);
                continue;
            }
            File.WriteAllBytes(tPath, www.bytes);
            Setup_FinishAssetCount++;
            yield return 0;
        }
        localAssetCache.Clear();
        PlayerPrefs.SetInt(firstSetupStr, 1);
    }
#endif

    #endregion

    #region Update
    public int localMainVersion;
    public int remoteMainVersion;

    public int _UPDATE_DOWNLOAD_MAX_COUNT_ = 0;
    public int _UPDATE_FINISH_COUNT_ = 0;
    public int _UPDATE_ERROR_COUNT_ = 0;
    public int _UPDATE_DONE_COUNT_
    {
        get { return _UPDATE_FINISH_COUNT_ + _UPDATE_ERROR_COUNT_; }
    }

    public enum UpdateStep
    {
        NONE,
        PARSE_LOCAL_XML,
        DOWNLOAD_AND_PARSE_REMOTE_XML,
        COMPARE,
        DEAL_SOFT_UPDATE,
        DEAL_HARD_UPDATE,
        DEAL_LOCAL_DELETE,
        UPDATE_DONE
    }
    public UpdateStep curUpdateStep = UpdateStep.NONE;

    Queue<Asset> softUpdateQueue = new Queue<Asset>(64);
    Queue<Asset> hardUpdateQueue = new Queue<Asset>(64);
    Queue<Asset> deleteQueue = new Queue<Asset>(64);

    private int _UPDATE_MAX_WWW_ = 4;
    private int currentDownloadAssetCount = 0;

    private string updateUrl = "";
    public void AsestUpdate(string url)
    {
        updateUrl = url;
        StartCoroutine(_AssetUpdate());
    }
    IEnumerator _AssetUpdate()
    {
        ParseLocalXmlFile();
        yield return 0;
        yield return StartCoroutine(DownloadAndParseRemoteXmlFile());
        Compare();
        yield return 0;
        DealSoftUpdateQueue();
        yield return 0;
        DealHardUpdateQueue();
        yield return 0;
        DealDeleteQueue();
        yield return 0;
        UpdateAssetDone();
        yield return 0;
    }

    void ParseLocalXmlFile()
    {
        curUpdateStep = UpdateStep.PARSE_LOCAL_XML;
        var xmlData = AssetXmlOpt.LoadXml(assetPath + assetXmlName);
        localMainVersion = xmlData.mainversion;
        foreach (var data in xmlData.assetDatas)
        {
            localAssetCache[data.assetUrl] = data;
        }
    }
    IEnumerator DownloadAndParseRemoteXmlFile()
    {
        curUpdateStep = UpdateStep.DOWNLOAD_AND_PARSE_REMOTE_XML;
        WWW www = new WWW(updateUrl + remoteAssetXmlName);
        yield return www;
        if (www.error != null)
        {
            Debug.LogError("Download Remote Xml File Failed. [" + www.error + "]");
            yield break;
        }
#if !UNITY_WEBPLAYER
        File.WriteAllBytes(assetPath + remoteAssetXmlName, www.bytes);
#endif
        var xmlData = AssetXmlOpt.LoadXml(assetPath + remoteAssetXmlName);
        remoteMainVersion = xmlData.mainversion;
        foreach (var data in xmlData.assetDatas)
        {
            remoteAssetCache[data.assetUrl] = data;
        }
    }
    void Compare()
    {
        curUpdateStep = UpdateStep.COMPARE;
        softUpdateQueue.Clear();
        hardUpdateQueue.Clear();
        deleteQueue.Clear();
        CompareXmlAssetsNeedUpdate();
        CompareXmlAssetNeedDelete();
    }
    void CompareXmlAssetsNeedUpdate()
    {
        foreach (var kvp in remoteAssetCache)
        {
            bool md5Change = false;
            bool versionChange = false;
            if (localAssetCache.ContainsKey(kvp.Key))
            {
                if (localAssetCache[kvp.Key].assetMD5 != kvp.Value.assetMD5)
                    md5Change = true;
                if (localAssetCache[kvp.Key].version != kvp.Value.version)
                    versionChange = true;
                if (md5Change)
                    hardUpdateQueue.Enqueue(kvp.Value);
                else
                    if (versionChange)
                    softUpdateQueue.Enqueue(kvp.Value);
            }
            else
            {
                hardUpdateQueue.Enqueue(kvp.Value);
            }
        }
        _UPDATE_DOWNLOAD_MAX_COUNT_ = hardUpdateQueue.Count;
        _UPDATE_FINISH_COUNT_ = 0;
        _UPDATE_ERROR_COUNT_ = 0;
    }

    void CompareXmlAssetNeedDelete()
    {
        foreach (var kvp in localAssetCache)
        {
            if (!remoteAssetCache.ContainsKey(kvp.Key))
                deleteQueue.Enqueue(kvp.Value);
        }
    }

    void DealSoftUpdateQueue()
    {
        curUpdateStep = UpdateStep.DEAL_SOFT_UPDATE;
        while (softUpdateQueue.Count > 0)
        {
            var asset = softUpdateQueue.Dequeue();
            AssetXmlOpt.ModifyAssetNode(asset, assetPath + assetXmlName);
        }
    }
    IEnumerator DealHardUpdateQueue()
    {
        curUpdateStep = UpdateStep.DEAL_HARD_UPDATE;
        currentDownloadAssetCount = 0;
        while (hardUpdateQueue.Count > 0)
        {
            if (currentDownloadAssetCount < _UPDATE_MAX_WWW_)
            {
                Asset asset = hardUpdateQueue.Dequeue();
                StartCoroutine(DownloadAsset(asset));
            }
            else
            {
                yield return 0;
            }
        }
    }

    IEnumerator DownloadAsset(Asset asset)
    {
        string fileUrl = asset.assetUrl;
        WWW www = new WWW(fileUrl);
        currentDownloadAssetCount++;
        yield return www;
        if (www.error != null)
        {
            _UPDATE_ERROR_COUNT_++;
            currentDownloadAssetCount--;
            yield break;
        }
        string filePath = (assetPath + asset.assetUrl).Replace(@"\", @"/");
        string fileDir = filePath.Substring(0, filePath.LastIndexOf(@"/"));
        if (!Directory.Exists(fileDir)) { Directory.CreateDirectory(fileDir); }
#if !UNITY_WEBPLAYER
        File.WriteAllBytes(assetPath + asset.assetUrl, www.bytes);
#endif
        currentDownloadAssetCount--;
        _UPDATE_FINISH_COUNT_++;
        if (AssetXmlOpt.FindAssetNode(asset.assetUrl, assetPath + assetXmlName))
        {
            AssetXmlOpt.ModifyAssetNode(asset, assetPath + assetXmlName);
        }
        else
        {
            AssetXmlOpt.AddAssetNode(asset, assetPath + assetXmlName);
        }
    }

    void DealDeleteQueue()
    {
        curUpdateStep = UpdateStep.DEAL_LOCAL_DELETE;
        while (deleteQueue.Count > 0)
        {
            var asset = deleteQueue.Dequeue();
            string filePath = assetPath + asset.assetUrl;
            if (File.Exists(filePath))
            {
                File.Delete(filePath);
            }
            AssetXmlOpt.DeleteAssetNode(asset, assetPath + assetXmlName);
        }
    }

    void UpdateAssetDone()
    {
        curUpdateStep = UpdateStep.UPDATE_DONE;
        AssetXmlOpt.ModifyAssetXmlMainVersion(remoteMainVersion, assetPath + assetXmlName);
        string remoteXmlUrl = assetPath + remoteAssetXmlName;
        if (File.Exists(remoteXmlUrl))
            File.Delete(remoteXmlUrl);
    }



#endregion



#region Singlton
    private static AssetsManager instance = null;
    public static void Initialize()
    {
        GameObject obj = new GameObject("AssetsManager");
        instance = obj.AddMissingComponent<AssetsManager>();
        obj.AddMissingComponent<AssetLoad.ResourceManager>();
#if !FIX_RESOURCE
        obj.AddMissingComponent<AssetLoad.AssetBundleManager>();
#endif
    }
    public static AssetsManager GetInstance() { return instance; }
    public static AssetsManager Instance
    {
        get { return instance; }
    }
#endregion
}
